#include "N2Bitmap.h"
#include "N2Geometry.h"
#include "N0Lib.h"

#ifndef WINDOWS

typedef struct tagBITMAP {
	LONG	bmType;
	LONG	bmWidth;
	LONG	bmHeight;
	LONG	bmWidthBytes;
	WORD	bmPlanes;
	WORD	bmBitsPixel;
	LPVOID	bmBits;
} BITMAP,*PBITMAP,*LPBITMAP;
typedef struct tagBITMAPCOREHEADER {
	DWORD	bcSize;
	WORD	bcWidth;
	WORD	bcHeight;
	WORD	bcPlanes;
	WORD	bcBitCount;
} BITMAPCOREHEADER,*LPBITMAPCOREHEADER,*PBITMAPCOREHEADER;
#pragma pack(push,1)
typedef struct tagRGBTRIPLE {
	BYTE rgbtBlue;
	BYTE rgbtGreen;
	BYTE rgbtRed;
} RGBTRIPLE,*LPRGBTRIPLE;
#pragma pack(pop)
#pragma pack(push,2)
typedef struct tagBITMAPFILEHEADER {
	WORD	bfType;
	DWORD	bfSize;
	WORD	bfReserved1;
	WORD	bfReserved2;
	DWORD	bfOffBits;
} BITMAPFILEHEADER,*LPBITMAPFILEHEADER,*PBITMAPFILEHEADER;
#pragma pack(pop)
typedef struct _BITMAPCOREINFO {
	BITMAPCOREHEADER	bmciHeader;
	RGBTRIPLE	bmciColors[1];
} BITMAPCOREINFO,*LPBITMAPCOREINFO,*PBITMAPCOREINFO;
typedef struct tagBITMAPINFOHEADER{
	DWORD	biSize;
	LONG	biWidth;
	LONG	biHeight;
	WORD	biPlanes;
	WORD	biBitCount;
	DWORD	biCompression;
	DWORD	biSizeImage;
	LONG	biXPelsPerMeter;
	LONG	biYPelsPerMeter;
	DWORD	biClrUsed;
	DWORD	biClrImportant;
} BITMAPINFOHEADER,*LPBITMAPINFOHEADER,*PBITMAPINFOHEADER;
typedef struct tagRGBQUAD {
	BYTE	rgbBlue;
	BYTE	rgbGreen;
	BYTE	rgbRed;
	BYTE	rgbReserved;
} RGBQUAD,*LPRGBQUAD;
typedef struct tagBITMAPINFO {
	BITMAPINFOHEADER bmiHeader;
	RGBQUAD bmiColors[1];
} BITMAPINFO,*LPBITMAPINFO,*PBITMAPINFO;

#endif


/*
 * Dib Header Marker - used in writing DIBs to files
 */
#define DIB_HEADER_MARKER   ((WORD) ('M' << 8) | 'B')

void N2SaveBitmap(FILE* file, const N2Drawer *drawer)
{
    uint32 nWidth, nHeight;

    BITMAPFILEHEADER bmFileHeader;
    LPBITMAPINFOHEADER lpbmInfoHeader;

    int nBPP;
    int PalSize;

    drawer->getSize().get(nWidth, nHeight);
    nBPP = drawer->getBitsPerPixel();

    uint32 nByteInRow =( nWidth * nBPP + 31) / 32 * 4;

    switch  (nBPP) 
    {
        case 1:  PalSize = 2;   break;
        case 4:  PalSize = 16;  break;
        case 8:  PalSize = 256; break;
        case 32: 
        case 24: PalSize = 0;   break;
    }

    bmFileHeader.bfType = DIB_HEADER_MARKER;
    bmFileHeader.bfSize = nByteInRow * nHeight + PalSize*4 +
                           sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
    bmFileHeader.bfReserved1 = 0;
    bmFileHeader.bfReserved2 = 0;
    bmFileHeader.bfOffBits = sizeof(BITMAPINFOHEADER) +
                             sizeof(BITMAPFILEHEADER) + PalSize*4;

    lpbmInfoHeader = (LPBITMAPINFOHEADER)new char[sizeof(BITMAPINFOHEADER) + PalSize*4];
  
    lpbmInfoHeader->biSize = sizeof(BITMAPINFOHEADER);// + PalSize*4;
    lpbmInfoHeader->biWidth = nWidth;
    lpbmInfoHeader->biHeight = nHeight;
    lpbmInfoHeader->biPlanes = 1;
    lpbmInfoHeader->biBitCount = nBPP;
    lpbmInfoHeader->biCompression = BI_RGB;
    lpbmInfoHeader->biSizeImage = 0;
    lpbmInfoHeader->biXPelsPerMeter = 0;
    lpbmInfoHeader->biYPelsPerMeter = 0;
    lpbmInfoHeader->biClrUsed = PalSize;
    lpbmInfoHeader->biClrImportant = 0;

    RGBQUAD *pRGB;

    long nColors = 0;

    switch  (nBPP) 
    {
        case 1:
            pRGB = (RGBQUAD*)((BYTE*)lpbmInfoHeader + sizeof(BITMAPINFOHEADER));

            (pRGB+0)->rgbBlue     = 0;
            (pRGB+0)->rgbGreen    = 0;
            (pRGB+0)->rgbRed      = 0;
            (pRGB+0)->rgbReserved = 0;

            (pRGB+1)->rgbBlue     = 255;
            (pRGB+1)->rgbGreen    = 255;
            (pRGB+1)->rgbRed      = 255;
            (pRGB+1)->rgbReserved = 0;

            nColors = 0;
            break;
        case 4:
            nColors = 16;
            break;
        case 8:
            nColors = 256;
            break;
    }

    do 
    {
        pRGB = (RGBQUAD*)((BYTE*)lpbmInfoHeader + sizeof(BITMAPINFOHEADER));
        N2Palette *pPal = drawer->getPalette();
        if(!pPal) break;
        for(long i=0;i<nColors;i++) 
        {
            COLORREF c = pPal->getColor(i);
            (pRGB+i)->rgbBlue     = GetBValue(c);
            (pRGB+i)->rgbGreen    = GetGValue(c);
            (pRGB+i)->rgbRed      = GetRValue(c);
            (pRGB+i)->rgbReserved = 0;
        }
    } while(0);

    // Write the file header
	fwrite(&bmFileHeader, sizeof(BITMAPFILEHEADER), 1, file);
	// Write the DIB header and the bits
	fwrite(lpbmInfoHeader, sizeof(BITMAPINFOHEADER) + PalSize*4, 1, file);


    BYTE *data;
    data = new BYTE [nByteInRow];

    long nColor;

    for(int j=nHeight-1;j>=0;j--) 
    {
        for(uint32 i=0;i<nWidth;i++) 
        {
            switch(nBPP) 
            {
                case 1:
                    if(drawer->getPixel(i, j))
                        *(data + i/8) |= (0x80>>(i%8));
                    else
                        *(data + i/8) &= (~(0x80>>(i%8)));
                    break;
                case 4:
                    nColor = drawer->getPixel(i, j) & 0xF;
                    *(data + i/2) &= ((0xF<<(4*(i%2))));
                    if(i&1)
                        *(data + i/2) |= nColor;
                    else
                        *(data + i/2) |= (nColor<<4);
                    break;
                case 8:
                    *(data + i) = (BYTE)drawer->getPixel(i, j);
                    break;
                case 24:
                    break;
                case 32:
                    *((DWORD*)data + i) = drawer->getPixel(i, j);
                    break;
            }
        }
        fwrite(data, nByteInRow, 1, file);
    }

    delete []data;
    delete lpbmInfoHeader;

    fflush(file);
}

void N2SaveBitmap(N1UnicodeString* path, const N2Drawer *drawer)
{
#ifdef WINDOWS
    FILE *file = _wfopen(*path, L"wb");
#else
    FILE* file = fopen(path->UTF8(), "wb")
#endif
    N2SaveBitmap(file, drawer);
    fclose(file);
}


N1NAME_DEF(N2Bitmap)

N2Bitmap::~N2Bitmap()
{
    if(m_palette)
    {
        m_palette->release();
    }
}

N2Bitmap* N2Bitmap::createFromBmpFile(N1AbstractFile *file)
{

    return NULL;
}
